package main

import (
	"fmt"
	"github.com/antlr/antlr4/runtime/Go/antlr"
	"parser"
	"strconv"
)

type EvalVisitor struct {
	*parser.BaseLabeledExprVisitor
	memory map[string]interface{}
}

func (v *EvalVisitor) VisitProg(ctx *parser.ProgContext) interface{} {
	return v.VisitChildren(ctx)
}

func (v *EvalVisitor) VisitChildren(node antlr.RuleNode) interface{} {
	for _, child := range node.GetChildren() {
		switch ctx := child.(type) {
		case *parser.ProgContext:
			v.VisitProg(ctx)
		case *parser.ParensContext:
			v.VisitParens(ctx)
		case *parser.AssignContext:
			v.VisitAssign(ctx)
		case *parser.PrintExprContext:
			v.VisitPrintExpr(ctx)
		case *parser.IntContext:
			v.VisitInt(ctx)
		case *parser.IdContext:
			v.VisitId(ctx)
		case *parser.MulDivContext:
			v.VisitMulDiv(ctx)
		case *parser.ClearContext:
			v.VisitClear(ctx)
		case *parser.AddSubContext:
			v.VisitAddSub(ctx)
		case *parser.BlankContext:
			v.VisitBlank(ctx)
		default:
			panic("Unknown context")
		}
	}

	return nil
}

// ID '=' expr NEWLINE # assign
func (v *EvalVisitor) VisitAssign(ctx *parser.AssignContext) interface{} {
	// id在'='的左侧
	id := ctx.ID().GetText()
	// 计算右侧表达式的值
	value := v.Visit(ctx.Expr())
	// 将这个映射关系存储在计算器的"内存"中
	v.memory[id] = value
	return value
}

// expr NEWLINE  #printExpr
func (v *EvalVisitor) VisitPrintExpr(ctx *parser.PrintExprContext) interface{} {
	// 计算expr子节点的值
	value := v.Visit(ctx.Expr())
	// 打印结果
	fmt.Println(value)
	// 上面已经直接打印除了结果，因此这里返回一个假值即可
	return nil
}

// INT
func (v *EvalVisitor) VisitInt(ctx *parser.IntContext) interface{} {
	val, err := strconv.Atoi(ctx.INT().GetText())
	if err != nil {
		fmt.Printf("err:%v", err)
	}
	return val
}

// ID
func (v *EvalVisitor) VisitId(ctx *parser.IdContext) interface{} {
	id := ctx.ID().GetText()
	if val, ok := v.memory[id]; ok {
		return val
	}
	return nil
}

// expr op=('*'|'/') expr # MulDiv
func (v *EvalVisitor) VisitMulDiv(ctx *parser.MulDivContext) interface{} {
	left := v.Visit(ctx.Expr(0))
	// 计算右侧子表达式的值
	right := v.Visit(ctx.Expr(1))
	if left != nil && right != nil {
		lv, ok := left.(int)
		rv, _ok := right.(int)
		if ok && _ok {
			switch ctx.GetOp().GetTokenType() {
			case parser.LabeledExprLexerMUL:
				return lv * rv
			case parser.LabeledExprLexerDIV:
				return lv / rv
			default:
				panic(fmt.Sprintf("unexpected op: %s", ctx.GetOp().GetText()))
			}
		}
	}

	return nil
}

// expr op=('+'|'-') expr # AddSub
func (v *EvalVisitor) VisitAddSub(ctx *parser.AddSubContext) interface{} {
	left := v.Visit(ctx.Expr(0))
	// 计算右侧子表达式的值
	expr := ctx.Expr(1)
	_ = expr
	right := v.Visit(ctx.Expr(1))
	if left != nil && right != nil {
		lv, ok := left.(int)
		rv, _ok := right.(int)
		if ok && _ok {
			switch ctx.GetOp().GetTokenType() {
			case parser.LabeledExprLexerADD:
				return lv + rv
			case parser.LabeledExprLexerSUB:
				return lv - rv
			default:
				panic(fmt.Sprintf("unexpected op: %s", ctx.GetOp().GetText()))
			}
		}
	}

	return nil
}

// '(' expr ')' # parens
func (v *EvalVisitor) VisitParens(ctx *parser.ParensContext) interface{} {
	// 返回字表达式的值
	return v.Visit(ctx.Expr())
}

/**
1、在map的数量级在10w以内的话，make方式会比delete方式速度更快，但是内存会消耗更多一点。
2、如果map数量级大于10w的话，delete的速度会更快，且内存消耗更少。
3、对于不再使用的map，直接使用make方式，长度为0清空更快。
*/
// clear
func (v *EvalVisitor) VisitClear(ctx *parser.ClearContext) interface{} {
	fmt.Printf("Before clear memory size %d\n", len(v.memory))
	for k := range v.memory {
		delete(v.memory, k)
	}
	fmt.Printf("After clear memory size %d\n", len(v.memory))
	return nil
}

func (v *EvalVisitor) Visit(tree antlr.ParseTree) interface{} {
	switch ctx := tree.(type) {
	case *parser.ProgContext:
		return v.VisitProg(ctx)
	case *parser.ParensContext:
		return v.VisitParens(ctx)
	case *parser.AssignContext:
		return v.VisitAssign(ctx)
	case *parser.PrintExprContext:
		return v.VisitPrintExpr(ctx)
	case *parser.IntContext:
		return v.VisitInt(ctx)
	case *parser.IdContext:
		return v.VisitId(ctx)
	case *parser.MulDivContext:
		return v.VisitMulDiv(ctx)
	case *parser.ClearContext:
		return v.VisitClear(ctx)
	case *parser.AddSubContext:
		return v.VisitAddSub(ctx)
	case *parser.BlankContext:
		return v.VisitBlank(ctx)
	default:
		panic("Unknown context")
	}
}

var _ parser.LabeledExprVisitor = &EvalVisitor{}
